package com.opNaga.bpmp.flowObject.gateway;

import com.opNaga.bpmp.BPMPElement;
import com.opNaga.bpmp.artifact.Groupable;
import com.opNaga.bpmp.artifact.Grouper;
import com.opNaga.bpmp.connectingObject.sequenceFlow.SequenceFlow;
import com.opNaga.bpmp.connectingObject.sequenceFlow.SequenceFlowSource;
import com.opNaga.bpmp.connectingObject.sequenceFlow.SequenceFlowTarget;
import com.opNaga.bpmp.flowObject.FlowObject;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * User: Junyi BEI
 * Date: 19/01/2012
 * Time: 14:05
 */
public abstract class Gateway extends FlowObject implements SequenceFlowSource, SequenceFlowTarget, Groupable {
  protected Set<BPMPElement> outwardSequenceFlows = new HashSet<BPMPElement>();
  protected Set<BPMPElement> inwardSequenceFlows = new HashSet<BPMPElement>();
  protected Set<BPMPElement> groupers = new HashSet<BPMPElement>();

  public Gateway() {
    super();
  }

  public Gateway(String t) {
    super(t);
  }

  @Override
  public boolean destroy() {
    Iterator<BPMPElement> outwardSequenceFlowIt = iterateOutwardSequenceFlows();
    while(outwardSequenceFlowIt.hasNext()) {
      BPMPElement be = outwardSequenceFlowIt.next();
      if(!be.destroy()) {
        return standardMessage(800, be);
      }
    }
    Iterator<BPMPElement> inwardSequenceFlowIt = iterateInwardSequenceFlows();
    while(inwardSequenceFlowIt.hasNext()) {
      BPMPElement be = inwardSequenceFlowIt.next();
      if(!be.destroy()) {
        return standardMessage(801, be);
      }
    }
    return super.destroy();
  }

  public boolean addOutwardSequenceFlow(BPMPElement be) {
    if(!(be instanceof SequenceFlow)) {
      return standardMessage(100, be);
    }
    if(!outwardSequenceFlows.add(be)) {
      return standardMessage(101, be);
    }
    return standardMessage(190, be);
  }

  public boolean removeOutwardSequenceFlow(BPMPElement be) {
    if(!(be instanceof SequenceFlow)) {
      return standardMessage(200, be);
    }
    if(!outwardSequenceFlows.remove(be)) {
      return standardMessage(201, be);
    }
    return standardMessage(290, be);
  }

  public boolean isOutwardSequenceFlowEmpty() {
    return outwardSequenceFlows.isEmpty();
  }

  public int outwardSequenceFlowSize() {
    return outwardSequenceFlows.size();
  }

  public Iterator<BPMPElement> iterateOutwardSequenceFlows() {
    return new ArrayList<BPMPElement>(outwardSequenceFlows).iterator();
  }

  public boolean addInwardSequenceFlow(BPMPElement be) {
    if(!(be instanceof SequenceFlow)) {
      return standardMessage(130, be);
    }
    if(!inwardSequenceFlows.add(be)) {
      return standardMessage(131, be);
    }
    return standardMessage(193, be);
  }

  public boolean removeInwardSequenceFlow(BPMPElement be) {
    if(!(be instanceof SequenceFlow)) {
      return standardMessage(230, be);
    }
    if(!inwardSequenceFlows.remove(be)) {
      return standardMessage(231, be);
    }
    return standardMessage(293, be);
  }

  public boolean isInwardSequenceFlowEmpty() {
    return inwardSequenceFlows.isEmpty();
  }

  public int inwardSequenceFlowSize() {
    return inwardSequenceFlows.size();
  }

  public Iterator<BPMPElement> iterateInwardSequenceFlows() {
    return new ArrayList<BPMPElement>(inwardSequenceFlows).iterator();
  }

  public boolean addGrouper(BPMPElement be) {
    if(!Grouper.class.isAssignableFrom(be.getClass())) {
      return standardMessage(670, be);
    }
    if(groupers.add(be)) {
      return standardMessage(671, be);
    }
    return standardMessage(692, be);
  }

  public boolean removeGrouper(BPMPElement be) {
    if(!Grouper.class.isAssignableFrom(be.getClass())) {
      return standardMessage(680, be);
    }
    if(groupers.remove(be)) {
      return standardMessage(681, be);
    }
    return standardMessage(693, be);
  }

  public Iterator<BPMPElement> iterateGroupers() {
    return new ArrayList<BPMPElement>(groupers).iterator();
  }

  public BPMPElement linkTo(BPMPElement be) {
    if(SequenceFlowTarget.class.isAssignableFrom(be.getClass()) && ((SequenceFlowTarget) be).getRoot() == root) {
      BPMPElement sf;
      if(manager != null) {
        if(be.getManager() == manager) {
          sf = manager.createInstance("sf", "-s=" + name, "-t=" + be.getName());
          if(sf != null) {
            standardMessage(910, be, sf);
            return sf;
          } else {
            standardMessage(903, be);
            return null;
          }
        } else {
          standardMessage(902, be);
          return null;
        }
      } else {
        sf = new SequenceFlow();
        if(!((SequenceFlow) sf).setTarget(be)) {
          sf.destroy();
          standardMessage(900, be, sf);
          return null;
        }
        if(!((SequenceFlow) sf).setSource(this)) {
          sf.destroy();
          standardMessage(901, be, sf);
          return null;
        }
        if(standardMessage(911, be, sf)) {
          return sf;
        }
      }
    }
    return super.linkTo(be);
  }

  public BPMPElement getLinkTo(BPMPElement be) {
    if(SequenceFlowTarget.class.isAssignableFrom(be.getClass()) && ((SequenceFlowTarget) be).getRoot() == root) {
      Iterator<BPMPElement> osfIt = iterateOutwardSequenceFlows();
      while(osfIt.hasNext()) {
        BPMPElement osf = osfIt.next();
        if(((SequenceFlow) osf).getTarget() == be) {
          standardMessage(950, be, osf);
          return osf;
        }
      }
      standardMessage(940, be);
      return null;
    }
    return super.getLinkTo(be);
  }

  public BPMPElement linkFrom(BPMPElement be) {
    if(SequenceFlowSource.class.isAssignableFrom(be.getClass()) && ((SequenceFlowSource) be).getRoot() == root) {
      BPMPElement sf;
      if(manager != null) {
        if(be.getManager() == manager) {
          sf = manager.createInstance("sf", "-s=" + be.getName(), "-t=" + name);
          if(sf != null) {
            standardMessage(930, be, sf);
            return sf;
          } else {
            standardMessage(923, be);
            return null;
          }
        } else {
          standardMessage(922, be);
          return null;
        }
      } else {
        sf = new SequenceFlow();
        if(!((SequenceFlow) sf).setTarget(this)) {
          sf.destroy();
          standardMessage(920, be, sf);
          return null;
        }
        if(!((SequenceFlow) sf).setSource(be)) {
          sf.destroy();
          standardMessage(921, be, sf);
          return null;
        }
        if(standardMessage(931, be, sf)) {
          return sf;
        }
      }
    }
    return super.linkFrom(this);
  }

  public BPMPElement getLinkFrom(BPMPElement be) {
    if(SequenceFlowSource.class.isAssignableFrom(be.getClass()) && ((SequenceFlowSource) be).getRoot() == root) {
      Iterator<BPMPElement> isfIt = iterateInwardSequenceFlows();
      while(isfIt.hasNext()) {
        BPMPElement isf = isfIt.next();
        if(((SequenceFlow) isf).getSource() == be) {
          if(standardMessage(970, be, isf)) {
            return isf;
          } else {
            return null;
          }
        }
      }
      standardMessage(960, be);
      return null;
    }
    return super.getLinkFrom(be);
  }
}
